#pragma once

#include "OutputMessageBufferHolder.h"
#include "MemoryAllocation.h"

#include <deque>
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>

#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <memory>

namespace AlgoTrading
{
	namespace ConnectionLibrary
	{

		class SocketNotificationHandler
		{
		public:
			//Derived class must implement this method to parse and handle incomming messages
			virtual void ParseMessageData(std::istream& stream) = 0;
			//Socket error notification. It's guaranteed that socket will be closed
			virtual void OnError(const boost::system::error_code& error) = 0;
			//disconnect notification
			virtual void OnDisconnect() = 0;
		};

		class SocketReaderWriter : public boost::enable_shared_from_this<SocketReaderWriter>
		{
		public:
			SocketReaderWriter(boost::asio::io_service& io_service, boost::asio::ip::tcp::socket& socket, SocketNotificationHandler& notificationHandler);
			virtual ~SocketReaderWriter(void);

			//Function for send data
			void SendBuffer(OutputMessageBufferHolderPtr outputMessageBufferHolder);
			//Functions which starts reading data from socket
			void StartReading();
			//Disconnect socket
			void Disconnect();

		private:
			//transport handle
			boost::asio::ip::tcp::socket& socket;
			boost::asio::io_service& io_service;

			//SocketNotificationHandler
			SocketNotificationHandler& notificationHandler;

			//Disconnection helpers
			bool disconnected;
			void DisconnectImpl();

			//Write functions and data members
			void PerformSendRequest(OutputMessageBufferHolderPtr outputMessageBufferHolder);
			void ActiveWrite();
			void HandleWrite(const boost::system::error_code& error,size_t bytes_transferred);

			std::deque<OutputMessageBufferHolderPtr> cacheForWriting;
			bool writeInProgress;
			int unsentBuffersCount;
			int sentBuffersCount;

			//Read data functions
			void PerformRead();
			void HandleReadDataLength(const boost::system::error_code& error);
			void HandleReadMessage(const boost::system::error_code& error);
			void ParseMessageData();

			//buffers for reading data
			volatile ::google::protobuf::uint32 messageLength;
			std::auto_ptr<boost::asio::streambuf> streamBuf;

			//memory allocation
			handler_allocator allocator;
			//strand for syncronization on socket(i.e. post, dispatch operations and socket async read\write)
			std::auto_ptr<boost::asio::io_service::strand> strand;
		};

	}
}
